﻿Option Explicit On
Option Strict On

Imports System.IO

Namespace PL
    Public MustInherit Class PLBase
        Implements IPL, IDisposable, IPLValidation


        ''' <summary>
        ''' Main DataTable (Parent) for the PL
        ''' </summary>
        ''' <remarks></remarks>
        Public WithEvents MetaDataTable As MetaDataTable

        ''' <summary>
        ''' Queryname to initialise the MetaDataTable 
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <ObsoleteAttribute("This property is being superseded try using QueryID instead.", False)>
        Public Property QueryName() As String

        ''' <summary>
        ''' Queryname to initialise the MetaDataTable 
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property QueryID() As System.Guid?


        '''' <summary>
        '''' Collection of all childs PL
        '''' </summary>
        '''' <value></value>
        '''' <returns></returns>
        '''' <remarks>Not USE</remarks>
        'Public Property PLChilds() As List(Of PLBase)

        Public Property SQLWhere() As String = Nothing

        Public Property GuidFieldSTR As String

        ''' <summary>
        ''' Result DataSet that contains the Main MetaDataTable and related Child
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Dataset() As DataSet Implements IPL.Dataset

        Public Property ID As Guid?  ' ? is reuqired to delcare nullable value and be able to verify NOthing value
        ''' <summary>
        ''' Error message from all Validation
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>Not USE yet</remarks>
        Public Property Validation As PLValidation Implements IPLValidation.Validation



        Public Sub New()
            Dataset = New DataSet()
            MetaDataTable = New MetaDataTable()
            Validation = New PLValidation()
        End Sub
        Public Sub New(ByVal queryName As String)
            Me.New()
            Me.MetaDataTable.QueryName = queryName
            Me.QueryName = queryName
            Me.Validation.Name = queryName
        End Sub

        Public MustOverride Function Delete() As Boolean Implements IPL.Delete

        ''' <summary>
        ''' Save all the Data to respective MetaDataTable in the DataSet. 
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overridable Function Save() As Boolean Implements IPL.Save
            Try
                ' Dim tableRelation As MetaDataTable
                'TestPoint: Voir MetaDataTable pour les BuildCUDCommands, AssignAdapterCommandForCUD
                Dim DelDataSet As DataSet
                Dim AddDataSet As DataSet
                Dim addChTable As DataTable

                If Me.Dataset.HasChanges Then
                    DelDataSet = Me.Dataset.GetChanges(DataRowState.Deleted)
                    AddDataSet = Me.Dataset.GetChanges(DataRowState.Added Or DataRowState.Modified)
                    If DelDataSet IsNot Nothing Then
                        For Each addChTable In DelDataSet.Tables
                            If Not addChTable.GetChanges() Is Nothing Then
                                DirectCast(Me.Dataset.Tables(addChTable.TableName), MetaDataTable).ApplyChanges(False, addChTable)
                            End If
                        Next
                    End If

                    'Save Added Record
                    If AddDataSet IsNot Nothing Then
                        For Each addChTable In AddDataSet.Tables
                            If Not addChTable.GetChanges() Is Nothing Then
                                DirectCast(Me.Dataset.Tables(addChTable.TableName), MetaDataTable).ApplyChanges(False, addChTable)

                            End If
                        Next
                    End If

                    Me.Dataset.AcceptChanges()
                    Return True
                Else
                    Return False
                End If

                'Next
            Catch sqlex As System.Data.SqlClient.SqlException
                'HACK: On Utilise les codes de SQL. Attention si les codes change entre ADO et/ou SQL
                'Me.DataSet.RejectChanges()
                Select Case sqlex.Number
                    Case 2601, 2627  'Unique Index/Constriant Violation
                        Throw New Exception("Record Already Exist, Operation Aborded" + vbCrLf + vbCrLf + vbCrLf, sqlex)
                    Case 547
                        Throw New Exception("This record is used as a reference on another entry. Please Verify" + vbCrLf + vbCrLf + vbCrLf + sqlex.Message)
                    Case Else
                        Throw New Exception(String.Format("Database Error: {0} #{1} ", sqlex.Message, sqlex.Number), sqlex)
                End Select
            Catch dbcex As System.Data.DBConcurrencyException
                'Me.DataSet.RejectChanges()
                Throw New Exception("Concurrency Issue, Record Already Modified. Please Validate Your Previous Entry" + vbCrLf + vbCrLf + vbCrLf, dbcex)
            Catch ex As Exception
                'Me.DataSet.RejectChanges()
                Throw New Exception(String.Format("Row Cannot Be Save: {0}. Make Sure You Have a CUD Command" + vbCrLf + vbCrLf + vbCrLf, ex.Message), ex)
            Finally


            End Try
            Return False
        End Function

        Public MustOverride Function Validate() As Boolean Implements IPLValidation.Validate
        Public Overridable Sub ValidateRow(ByVal datarow As DataRow) Implements IPLValidation.ValidateRow
        End Sub

        ''' <summary>
        ''' OutStandingIssue:
        ''' Ce InitData Pacours les tables de facon hiérachique a un niveau... aleblanc
        ''' Parent
        '''    Enfant, Enfant, Enfant, Enfant
        ''' Voir Metacommand_PL pour une autre facon...
        ''' </summary>
        ''' <param name="withChild"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overridable Function InitData(Optional ByVal withChild As Boolean = True) As Boolean Implements IPL.InitData
            Dim aChildRelationField As String
            Dim DataViewAccess As MetaQueryAccess
            Dim aDR As DataRelation
            Dim metaDataTableChild As CommonExt.MetaDataTable
            Dim childQuery As String

            Try
                Me.Dataset = New DataSet()
                Me.MetaDataTable = New MetaDataTable()
                Me.Validation = New PLValidation()
                Me.Validation.Name = QueryName

                Me.MetaDataTable.QueryName = Me.QueryName
                If Not String.IsNullOrEmpty(Me.SQLWhere) Then

                    Me.MetaDataTable.GetMetaCommand()
                    If Not String.IsNullOrEmpty(Me.MetaDataTable.MetaCommand.SQLWhere) Then
                        Me.MetaDataTable.MetaCommand.SQLWhere += " AND "
                    End If
                    Me.MetaDataTable.MetaCommand.SQLWhere += String.Format(" {0}", Me.SQLWhere)
                Else
                    Me.MetaDataTable.GetMetaCommand()
                End If
                'TestPoint: Get the SqlStatement in immediate widow with, ?Me.MetaDataTable.MetaCommand.SQLStatement
                Me.MetaDataTable.FillBusinessApplicationData()
                'on assigne la meta Sécurity des MetaQuery définie par les MetaControls sur la dataview 
                DataViewAccess = New MetaQueryAccess(MetaDataTable.MetaCommand.MetaQuerieCD)
                'Testpoint: if security is not define properly in metacontrol the MetaQueryAccess always return FALSE for all of it's properties by design B.Desjardins 2012-05-16...
                MetaDataTable.DefaultView.AllowNew = DataViewAccess.AllowCreate
                MetaDataTable.DefaultView.AllowEdit = DataViewAccess.AllowUpdate
                MetaDataTable.DefaultView.AllowDelete = DataViewAccess.AllowDelete
                'oustanding: y reste le allowRead ,BD 2012-05-16


                Me.Dataset.Tables.Add(Me.MetaDataTable)
                Me.GuidFieldSTR = MetaDataTable.MetaCommand.GUIDField


                '
                'Refactoring Child: 1- A enlever pour ne pas loader les child
                '
                If withChild Then

                    'HACK 1
                    'TestPoint 567: On attache les MetaQuery avec le bon PL.
                    'Ce qui nous permet de garder le ordering de nos Onglet "Child".
                    metaDataTableChild = Nothing
                    Dim aIdentWhere As String = Me.SQLWhere
                    For Each row As DataRow In Me.MetaDataTable.MetaCommand.GetChildDisplayQuery.Rows
                        Dim parentmetaquery As MetaQuery = New MetaQuery
                        Me.MetaDataTable.MetaCommand.CopyTo(parentmetaquery)
                        'Each Row has a MetaqueryLevel on 1 based index 
                        'when the level increase we will reuse the initial Me.MetaDataTable.MetaCommand to store the metaDataTableChild.MetaCommand
                        'this will permit hiarchie for the subsequent Me.MetaDataTable.MetaCommand.GetSQLForSubSelect
                        If CInt(row("MetaqueryLevel").ToString) > 1 Then
                            aIdentWhere = metaDataTableChild.MetaCommand.SQLWhere
                            metaDataTableChild.MetaCommand.CopyTo(parentmetaquery)
                        End If

                        If CInt(row("RelationOrder")) >= 0 Then
                            childQuery = row("QueryName").ToString()
                            'aChildRelationField = String.Format("{0}_FK", row("ParentGUIDField").ToString)
                            aChildRelationField = row("FKColumn_Name").ToString

                            metaDataTableChild = New CommonExt.MetaDataTable() With {.QueryName = row("QueryName").ToString}
                            'Ici on chaine nos WHERE Clause, pour que les enfants soit scopper sur le parent
                            '
                            metaDataTableChild.GetMetaCommand()
                            DataViewAccess = New MetaQueryAccess(metaDataTableChild.MetaCommand.MetaQuerieCD)
                            'if the control is define and you have access
                            If DataViewAccess.AllowRead = CShort(False) Then
                                metaDataTableChild.MetaCommand.SQLWhere = "1=2"
                            Else
                                'Scoped by a treelist or showing one record at the time
                                'Parent ID is filtered with a where clause
                                If Not String.IsNullOrEmpty(aIdentWhere) Then
                                    If Not String.IsNullOrEmpty(metaDataTableChild.MetaCommand.SQLWhere) Then
                                        metaDataTableChild.MetaCommand.SQLWhere += " AND "
                                    End If
                                    metaDataTableChild.MetaCommand.SQLWhere += String.Format(" {0} IN ({1})", row("FKColumn_Name").ToString, parentmetaquery.GetSQLForSubSelect(row("PKColumn_Name").ToString))
                                End If
                            End If

                            metaDataTableChild.FillBusinessApplicationData()
                            metaDataTableChild.DefaultView.AllowNew = DataViewAccess.AllowCreate
                            metaDataTableChild.DefaultView.AllowEdit = DataViewAccess.AllowUpdate
                            metaDataTableChild.DefaultView.AllowDelete = DataViewAccess.AllowDelete

                            Me.Dataset.Tables.Add(metaDataTableChild)
                            aDR = New DataRelation(row("Relation").ToString,
                                                    Me.Dataset.Tables(row("ParentQueryName").ToString).Columns(row("PKColumn_Name").ToString),
                                                    metaDataTableChild.Columns(aChildRelationField),
                                                    True)
                            Me.Dataset.Relations.Add(aDR)

                        End If

                    Next
                End If

                Return True
            Catch ex As Exception
                'TestPoint: Quand vous avez des écran qui load pas (Ecran Gris). Metez un breakpoint ici.
                'assurez vous que les champs retourne des valeurs
                'Ex: row("FKColumn_Name").ToString
                '    row("PKColumn_Name").ToString
                Throw
            End Try

            Return False
        End Function

        ''' <summary>
        ''' Add OR Expression to the Current SqlWhere Clause
        ''' </summary>
        ''' <param name="sqlWhereExpression"></param>
        ''' <remarks></remarks>
        Public Sub AddSQLWhereOr(ByVal sqlWhereExpression As String)
            If Not String.IsNullOrWhiteSpace(Me.SQLWhere) Then
                Me.SQLWhere = String.Format(" {0} OR ", Me.SQLWhere)
            End If
            Me.SQLWhere = String.Format(" {0} {1} ", Me.SQLWhere, sqlWhereExpression)
        End Sub

        ''' <summary>
        '''  Add AND Expression to the Current SqlWhere Clause
        ''' </summary>
        ''' <param name="sqlWhereExpression"></param>
        ''' <remarks></remarks>
        Public Sub AddSQLWhereAnd(ByVal sqlWhereExpression As String)
            If Not String.IsNullOrWhiteSpace(Me.SQLWhere) Then
                Me.SQLWhere = String.Format(" {0} AND ", Me.SQLWhere)
            End If
            Me.SQLWhere = String.Format(" {0} {1} ", Me.SQLWhere, sqlWhereExpression)
        End Sub


#Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    Dataset.Dispose()
                    Dataset = Nothing
                    MetaDataTable.Dispose()
                    MetaDataTable = Nothing
                    Validation.Messages.Dispose()
                    Validation = Nothing
                    ' TODO: dispose managed state (managed objects).
                End If
                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub

        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub

        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region
        ''' <summary>
        ''' Create an intance of a PL, So we can implement Factory Pattern on our PL Class
        ''' </summary>
        ''' <param name="typeOfPL"></param>
        ''' <param name="queryName"></param>
        ''' <param name="typeOfInit"></param>
        ''' <param name="factory"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function CreateInstanceOf(ByVal typeOfPL As PLTypeOf, Optional ByVal queryName As String = Nothing, Optional ByVal typeOfInit As PLInitType = PLInitType.Full, Optional ByVal factory As Boolean = True) As PLBase
            Return PLBase.CreateInstanceOf(typeOfPL.ToString, queryName, typeOfInit, factory)

        End Function


        Public Shared Function CreateInstanceOf(ByVal PLName As String, Optional ByVal queryName As String = Nothing, Optional ByVal typeOfInit As PLInitType = PLInitType.Full, Optional ByVal factory As Boolean = True) As PLBase
            'How to Load a class from an assembly
            'http://msdn.microsoft.com/en-us/library/wccyzw83.aspx
            'Imports System
            'Class DynamicInstanceList
            '            Private Shared instanceSpec As String = "System.EventArgs;System.Random;" + _
            '                "System.Exception;System.Object;System.Version"

            '            Public Shared Sub Main()
            '                Dim instances() As String = instanceSpec.Split(";")
            '                Dim instlist As Array = Array.CreateInstance(GetType(Object), instances.Length)
            '                Dim item As Object

            '                For i As Integer = 0 To instances.Length - 1
            '                    ' create the object from the specification string
            '                    Console.WriteLine("Creating instance of: {0}", instances(i))
            '                    item = Activator.CreateInstance(Type.GetType(instances(i)))
            '                    instlist.SetValue(item, i)
            '                Next i
            '                Console.WriteLine(vbNewLine + "Objects and their default values:" + vbNewLine)
            '                For Each o As Object In instlist
            '                    Console.WriteLine("Type:     {0}" + vbNewLine + "Value:    {1}" + _
            '                        vbNewLine + "HashCode: {2}" + vbNewLine, _
            '                        o.GetType().FullName, o.ToString(), o.GetHashCode())
            '                Next o
            '            End Sub
            'End Class



            'Find where the assembly is
            Dim assemblyUri As Uri = New Uri(Reflection.Assembly.GetExecutingAssembly().CodeBase)
            'Get the path
            Dim currentPath As String = Path.GetDirectoryName(assemblyUri.LocalPath)
            Dim typeFound As Boolean = False
            '
            'Default if there is no SDK.
            Dim sdkList As String = "SystemBoxSDK.dll"
            Dim instancePL As PLBase = Nothing
            '
            Dim pluginAssembly As Reflection.Assembly
            Dim assemblyTypes As System.Type()
            '
            'Configured Default SDK
            If Not String.IsNullOrWhiteSpace(AppManager.DefaultSDK) Then
                sdkList = AppManager.DefaultSDK
            End If
            '
            'Configured Client SDK when we use Factory
            If factory Then
                If Not String.IsNullOrWhiteSpace(AppManager.ClientSDK) Then
                    sdkList = String.Format("{0},{1}", AppManager.ClientSDK, sdkList)
                End If

            End If
            '
            For Each sdk As String In sdkList.Split(","c)
                For Each file As String In IO.Directory.GetFiles(currentPath, sdk)
                    'load the assembly file
                    pluginAssembly = Reflection.Assembly.LoadFrom(file)
                    'Get all then class in the assembly
                    assemblyTypes = pluginAssembly.GetTypes()
                    'Scan to get the class in the assembly
                    For Each type As System.Type In assemblyTypes
                        'Check if Class found, make sure the class name is unique for all NameSpace
                        If type.Name = PLName Then
                            'Create the object as instantePL, if the type found is of type PLBase
                            instancePL = TryCast(Activator.CreateInstance(type, False), PLBase)
                            If instancePL IsNot Nothing Then
                                instancePL.QueryName = queryName
                                typeFound = True
                                Exit For
                            End If
                        End If
                    Next
                    If typeFound = True Then Exit For
                Next
                If typeFound = True Then Exit For
            Next
            '
            Return instancePL
        End Function




        ''' <summary>
        ''' Return an IPLProcess type from an instance of a PLBase
        ''' </summary>
        ''' <param name="typeOfPL"></param>
        ''' <returns>Return an IPLProcess type if the class implement IPLProcess else Nothing</returns>
        ''' <remarks>ShortCut so you don't have to cast your line every time</remarks>
        Public Shared Function CreateIPLProcessOf(ByVal typeOfPL As PLTypeOf) As IPLProcess
            Return TryCast(PLBase.CreateInstanceOf(typeOfPL), IPLProcess)
        End Function
    End Class

End Namespace
